package accounts

import (
	"crypto/ecdsa"
	"fmt"
	"math/big"
	"math/rand"
	"strings"
	"time"

	"gitee.com/thubcc/blockchain/accounts/ecc"
	"github.com/ethereum/go-ethereum/accounts/keystore"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/secp256k1"
)

type KeyGenerator interface {
	PrivateKey2Address(*big.Int) string
}

type Config struct {
	DataDir string
	curve   KeyGenerator
	max     int64
}

func NewConfig(dir string,lib bool,m int64) *Config{
	var curve KeyGenerator
	if lib {
		curve = NewLibSecp256()
	} else {
		curve = ecc.NewSecp256K1()
	}
	return &Config{dir,curve,m}
}

type libSecp256 struct{
	c *secp256k1.BitCurve
	ecc *ecc.ECC
}

func NewLibSecp256() *libSecp256 {
	return &libSecp256{secp256k1.S256(),ecc.NewSecp256K1()}
}

func(curve *libSecp256) PrivateKey2Address(prk *big.Int) string {
	x,y := curve.c.ScalarBaseMult(prk.Bytes())
	puK :=&ecc.ECPoint{*x,*y}
	return curve.ecc.PublicKey2Address(puK)
}

func(c *Config) KeyDir() string {
	return c.DataDir+"/keystore"
}

func(c *Config) ReadKey(addr,passphrase string) (*ecdsa.PrivateKey,error) {
	var address = common.HexToAddress(addr)
	
	ks := keystore.NewKeyStore(c.KeyDir(), 262144, 1)
	accounts := ks.Accounts()
	for i,a := range accounts {
		if address.Hex() == a.Address.Hex() {
			if keyjson, err := ks.Export(accounts[i], passphrase, passphrase); err != nil {
				return nil,fmt.Errorf("Exort: %s",err)
			} else {
				if key,err := keystore.DecryptKey(keyjson, passphrase); err != nil {
					return nil,fmt.Errorf("Decrypt: %s",err)
				} else {
					return key.PrivateKey,nil
				}
			}
		} 
	}
	return nil,fmt.Errorf("no find: %s",addr)
}

func(c *Config) importKey(privkey *big.Int,passphrase string) (*common.Address,error) {
	if pk,err := crypto.ToECDSA(privkey.Bytes()); err!=nil {
		return nil,fmt.Errorf("Convert: %s",err)
	} else {
		ks := keystore.NewKeyStore(c.KeyDir(), 262144, 1)
		if a,err := ks.ImportECDSA(pk,passphrase); err!=nil {
			return nil,fmt.Errorf("Import: %s",err)
		} else {
			return &a.Address,nil
		}
	}
}

func(c *Config) ImportKey(keyStr,passphrase string) (*common.Address,error) {
	privkey := new(big.Int)
	privkey.SetString(keyStr,16)
	return c.importKey(privkey,passphrase)
}

func(c *Config) LuckyPrivateKey(start,end string, max int64) (*big.Int,error) {
	rnd := rand.New(rand.NewSource(time.Now().Unix()))
	prK := big.NewInt(0)
	
	prK.Rand(rnd, &ecc.NewSecp256K1().N)
	
	for i:=int64(0);i<max;i++ {
		key := c.curve.PrivateKey2Address(prK)
		if key[:len(start)]==start && key[len(key)-len(end):]==end {
			return prK,nil
		}
		prK.Add(prK,big.NewInt(1))
	}
	return nil,fmt.Errorf("Can not find Lucky Key")
}

func(c *Config) LuckyKey(start,end,passphrase string) (*common.Address,error) {
	privkey,err := c.LuckyPrivateKey(start,end,c.max)
	if err != nil {
		return nil,err
	}
	return c.importKey(privkey,passphrase)
}

func(c *Config) LuckyStar(start,passphrase string) (*common.Address,error) {
	return c.LuckyKey(start,"",passphrase)
}

func(c *Config) LuckyEnd(end,passphrase string) (*common.Address,error) {
	return c.LuckyKey("",end,passphrase)
}

func(c *Config) FindKey(start,passphrase string) (*ecdsa.PrivateKey,error) {
	ks := keystore.NewKeyStore(c.KeyDir(), 262144, 1)
	lstart := strings.ToLower(start)
	accounts := ks.Accounts()
	for i,a := range accounts {
		la := strings.ToLower(a.Address.Hex()[2:])
		if strings.HasPrefix(la,lstart) {
			if keyjson, err := ks.Export(accounts[i], passphrase, passphrase); err != nil {
				return nil,fmt.Errorf("Exort: %s",err)
			} else {
				if key,err := keystore.DecryptKey(keyjson, passphrase); err != nil {
					return nil,fmt.Errorf("Decrypt: %s",err)
				} else {
					return key.PrivateKey,nil
				}
			}
		} 
	}
	return nil,fmt.Errorf("no find: %s",start)
}
